#include "Dri_DMA.h"

/**
 * @brief DMA 初始化
 * 
 */
void Dri_DMA_Init(void)
{
    // 1. 时钟使能 -------------------------------------------
    // 使能 DMA1 时钟
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    // 2. DMA1 设置 ------------------------------------------
    // 2.1 设置通道5数据传输方向是外设到RAM (CCR5.DIR=0)
    DMA1_Channel5->CCR &= ~DMA_CCR5_DIR;
    // 2.2 设置RAM的数据位宽是8位 (CCR5.MSIZE=00)
    DMA1_Channel5->CCR &= ~DMA_CCR5_MSIZE;
    // 2.3 设置外设的数据位宽是8位 (CCR5.PSIZE=00)
    DMA1_Channel5->CCR &= ~DMA_CCR5_PSIZE;
    // 2.4 设置内存开启地址增量模式 (CCR5.MINC=1)
    DMA1_Channel5->CCR |= DMA_CCR5_MINC;
    // 2.5 设置外设不开启地址增量模式 (CCR5.PINC=0)
    DMA1_Channel5->CCR &= ~DMA_CCR5_PINC;
    // 2.6 设置通道的优先级为最高 (CCR5.PL=11)
    DMA1_Channel5->CCR |= DMA_CCR5_PL;
    // 2.7 开启传输完成中断 (CCR5.TCIE=1)
    DMA1_Channel5->CCR |= DMA_CCR5_TCIE;
    // 2.8 开启循环模式
    DMA1_Channel5->CCR |= DMA_CCR5_CIRC;

    // 3. USART1 设置 ------------------------------------------
    // 开启 USART1 中的 DMA 接收功能 (USART将接收到的数据通过DMA传输给RAM)
    USART1->CR3 |= USART_CR3_DMAR;

    // 4. NVIC 设置 -------------------------------------------
    NVIC_SetPriorityGrouping(3);
    NVIC_SetPriority(DMA1_Channel5_IRQn, 6);
    NVIC_EnableIRQ(DMA1_Channel5_IRQn);
}

/**
 * @brief DMA 数据传输
 * 
 * @param maddr 存储器地址
 * @param paddr 外设地址
 * @param len   数据长度
 */
void Dri_DMA_Transfer(uint32_t maddr, uint32_t paddr, uint16_t len)
{
    // 1. 设置存储器地址
    DMA1_Channel5->CMAR = maddr;

    // 2. 设置外设地址
    DMA1_Channel5->CPAR = paddr;

    // 3. 设置传输数据长度 (需要在通道开启前设置)
    DMA1_Channel5->CNDTR = len;

    // 4. 启动通道
    DMA1_Channel5->CCR |= DMA_CCR5_EN;
}

/*******************************************************************/
// 定义DMA1输出传输完成后调用的弱函数
__weak void Dri_DMA_TC_Callback(void)
{}

// 定义DMA1_Channel5中断服务函数
void DMA1_Channel5_IRQHandler(void)
{
    // 1. 清除TC中断标志位
    DMA1->IFCR |= DMA_IFCR_CTCIF5;

    // 2. 调用弱函数
    Dri_DMA_TC_Callback();
}
